package io.debezium.connector.postgresql.connection;

import com.huawei.gaussdb.jdbc.geometric.PGbox;
import com.huawei.gaussdb.jdbc.geometric.PGcircle;
import com.huawei.gaussdb.jdbc.geometric.PGline;
import com.huawei.gaussdb.jdbc.geometric.PGpath;
import com.huawei.gaussdb.jdbc.geometric.PGpoint;
import com.huawei.gaussdb.jdbc.geometric.PGpolygon;
import com.huawei.gaussdb.jdbc.util.PGmoney;
import io.debezium.connector.postgresql.PostgresStreamingChangeEventSource;
import io.debezium.connector.postgresql.PostgresType;
import io.debezium.connector.postgresql.TypeRegistry;

import java.time.Instant;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.util.List;
import java.util.OptionalLong;

public interface ReplicationMessage {
    Operation getOperation();

    Instant getCommitTime();

    OptionalLong getTransactionId();

    String getTable();

    List<Column> getOldTupleList();

    List<Column> getNewTupleList();

    boolean hasTypeMetadata();

    boolean isLastEventForLsn();

    default boolean shouldSchemaBeSynchronized() {
        return true;
    }

    default boolean isTransactionalMessage() {
        return this.getOperation() == ReplicationMessage.Operation.BEGIN
                || this.getOperation() == ReplicationMessage.Operation.COMMIT;
    }

    public static enum Operation {
        INSERT,
        UPDATE,
        DELETE,
        TRUNCATE,
        MESSAGE,
        BEGIN,
        COMMIT,
        NOOP;

        private Operation() {}
    }

    public static class NoopMessage implements ReplicationMessage {
        private final Long transactionId;
        private final Instant commitTime;
        private final Operation operation;

        public NoopMessage(Long transactionId, Instant commitTime) {
            this.operation = ReplicationMessage.Operation.NOOP;
            this.transactionId = transactionId;
            this.commitTime = commitTime;
        }

        public boolean isLastEventForLsn() {
            return true;
        }

        public boolean hasTypeMetadata() {
            throw new UnsupportedOperationException();
        }

        public OptionalLong getTransactionId() {
            return this.transactionId == null
                    ? OptionalLong.empty()
                    : OptionalLong.of(this.transactionId);
        }

        public String getTable() {
            throw new UnsupportedOperationException();
        }

        public Operation getOperation() {
            return this.operation;
        }

        public List<Column> getOldTupleList() {
            throw new UnsupportedOperationException();
        }

        public List<Column> getNewTupleList() {
            throw new UnsupportedOperationException();
        }

        public Instant getCommitTime() {
            return this.commitTime;
        }
    }

    public interface ColumnValue<T> {
        T getRawValue();

        boolean isNull();

        String asString();

        Boolean asBoolean();

        Integer asInteger();

        Long asLong();

        Float asFloat();

        Double asDouble();

        Object asDecimal();

        LocalDate asLocalDate();

        OffsetDateTime asOffsetDateTimeAtUtc();

        Instant asInstant();

        Object asTime();

        Object asLocalTime();

        OffsetTime asOffsetTimeUtc();

        byte[] asByteArray();

        PGbox asBox();

        PGcircle asCircle();

        Object asInterval();

        PGline asLine();

        Object asLseg();

        PGmoney asMoney();

        PGpath asPath();

        PGpoint asPoint();

        PGpolygon asPolygon();

        boolean isArray(PostgresType var1);

        Object asArray(
                String var1,
                PostgresType var2,
                String var3,
                PostgresStreamingChangeEventSource.PgConnectionSupplier var4);

        Object asDefault(
                TypeRegistry var1,
                int var2,
                String var3,
                String var4,
                boolean var5,
                PostgresStreamingChangeEventSource.PgConnectionSupplier var6);
    }

    public interface ColumnTypeMetadata {
        int getLength();

        int getScale();
    }

    public interface Column {
        String getName();

        PostgresType getType();

        ColumnTypeMetadata getTypeMetadata();

        Object getValue(PostgresStreamingChangeEventSource.PgConnectionSupplier var1, boolean var2);

        boolean isOptional();

        default boolean isToastedColumn() {
            return false;
        }
    }
}
